package ;
import js.Browser;
import js.html.DivElement;
import js.html.Document;
import js.html.Element;
import js.html.HtmlElement;
import js.html.Image;
import js.JQuery;
import Carousel;
import Item;
import js.Lib;
import me.cunity.debug.Out;
import Reflection;
import js.JQuery.JQueryHelper.*;

/**
 * ...
 * @author axel@cunity.me
 * 
 * inspired by code from:
 * CloudCarousel V1.0.5 - (c) 2011 by R Cecco. <http://www.professorcloud.com> - MIT License
 *
 */
@:keep
class Controller
{
	var items:Array<Item>;
	var ctx:Controller;
	var controlTimer:Int;
	var stopped:Bool;
	var container:JQuery;
	//var container:Element;
	var xRadius:Float;
	var yRadius:Float;
	var showFrontTextTimer:Int;
	var autoRotateTimer:Int;
	var xCentre:Float;
	var yCentre:Float;
	var frontIndex:Int;
	var rotation:Float;
	var destRotation:Float;
	var stepRotation:Float;
	var timeDelay:Int;
	var innerWrapper:Element;
	//var showFrontText:Void->Void;
	var tt:Int;
	//var images :JQuery;
	var images:Array<Image>;
	var options:Options;
	var isMSIE:Bool;
	var hasCanvas:Bool;
	var firstLoop:Bool;
	public static var logLoop:Int;
	public static var logData:Array<Int>;
	
	public function new(carousel:JQuery, images:JQuery, options:Options) 
	{
		ctx = this;
		logLoop = 0;
		logData = new Array();
		isMSIE = JQuery.browser.msie;
		items = new Array();
		controlTimer = 0;
		stopped = false;
		container = carousel;
		xRadius = options.xRadius;
		yRadius = options.yRadius;
		showFrontTextTimer = 0;
		autoRotateTimer = 0;
		this.images = cast images.get();
		this.options = options;
		
		this.stepRotation = ( Math.PI / this.images.length ) * 2;
		var marginY:Int = Math.round((carousel.parent().height() - 2 * xRadius) / 2);
		if (marginY < 0)
			marginY = 0;
		xCentre = options.xPos + marginY;
		yCentre = options.yPos;
		frontIndex = 0;	// Index of the item at the front		
		trace('this.xCentre:' + this.xCentre + ' yCentre:' + this.yCentre + ' items:' + images.length);
		firstLoop = true;

		// Start with the first item at the front.
		rotation = destRotation = Math.PI/2;
		timeDelay = Math.round(1000/options.FPS);
								
		// Turn on the infoBox
		if(options.altBox != null)
		{
			J('#' + options.altBox + '.altBox').css({display:'block', visibility:'visible', opacity:1.0});	
			trace('#' + options.altBox + '.altBox' + ':' + J('#' + options.altBox).length);
			Out.dumpJLayout(J('#' + options.altBox + '.altBox'));
			J(options.titleBox).css('display','block');	
		}	
		
		// Turn on relative position for container to allow absolutely positioned elements
		// within it to work.
		J(container).css({ position:'relative', overflow:'hidden'} );
	
		J(options.buttonLeft).css('display','inline');
		J(options.buttonRight).css('display','inline');
		
		// Setup the buttons.
		J(options.buttonLeft).bind('mouseup',this,function(event){
			event.data.rotate(-1);	
			return false;
		});
		J(options.buttonRight).bind('mouseup',this,function(event){															
			event.data.rotate(1);	
			return false;
		});
		
		// You will need this plugin for the mousewheel to work:http://plugins.jquery.com/project/mousewheel
		if (options.mouseWheel)
		{
			J(container).bind('mousewheel',this,function(event, delta) {					 
					 event.data.rotate(delta);
					 return false;
				 });
		}
		J(container).bind('mouseover click', this, function(event)
		{			
			Browser.window.clearInterval(event.data.autoRotateTimer);		// Stop auto rotation if mouse over.
			var	text = J(event.target).attr('alt');		
			// If we have moved over a carousel item, then show the alt and title text.
			//debug(text);
			if ( text != null && text != null )
			{					
				Browser.window.clearTimeout(event.data.showFrontTextTimer);			
				//J(options.altBox).html(  J(event.target).attr('alt') );
				J('#' + options.altBox + '.altBox').html( '<div class="inlineLeft">' + J(event.target).attr('alt')  + '</div>');
				J(options.titleBox).html( (J(event.target).attr('title') ));							
				if ( options.bringToFront && event.type == 'click' )				
				{
				
					var	idx = J(event.target).data('itemIndex');	
					var	frontIndex = event.data.frontIndex;
					//var	diff = idx - frontIndex;                    
                    var        diff = (idx - frontIndex) % images.length;
                    if (Math.abs(diff) > images.length / 2) {
                        diff += (diff > 0 ? -images.length :images.length);
                    }
                    
					event.data.rotate(-diff);
				}
			}
		});
		// If we have moved out of a carousel item (or the container itself),
		// restore the text of the front item in 1 second.
		J(container).bind('mouseout',this,function(event){
				var	context = event.data;				
				Browser.window.clearTimeout(context.showFrontTextTimer);				
				context.showFrontTextTimer = Browser.window.setTimeout( function(){context.showFrontText();},1000);
				context.autoRotate();	// Start auto rotation.
		});

		// Prevent items from being selected as mouse is moved and clicked in the container.
		J(container).bind('mousedown',this,function(event){	
			
			event.data.container.focus();
			return false;
		});
		untyped container.onselectstart = function () { return false; };		// For IE.

		innerWrapper = J(container).wrapInner('<div style="position:absolute;width:100%;height:100%;"/>').children()[0];
		
		trace(innerWrapper);
		
		
		tt = Browser.window.setInterval( function(){ctx.checkImagesLoaded();},50);			
	}
	
	// Shows the text from the front most item.
	function showFrontText()
	{	
		if ( items[this.frontIndex] == null ) { return; }	// Images might not have loaded yet.
		J(options.titleBox).html( J(items[this.frontIndex].image).attr('title'));
		//J(options.altBox).html( J(items[this.frontIndex].image).attr('alt'));
		J('#' + options.altBox + '.altBox').html( '<div class="inlineLeft">' + J(items[this.frontIndex].image).attr('alt') + '</div>');
		//debug(J(options.altBox).length + 'altBox.text:' + J(options.altBox).html());
	};
					
	function go()
	{				
		if(this.controlTimer != 0) { return; }
		var	context = this;
		this.controlTimer = Browser.window.setTimeout( function(){context.updateAll();},this.timeDelay);					
	};
	
	function stop()
	{
		Browser.window.clearTimeout(this.controlTimer);
		this.controlTimer = 0;				
	};
	
	
	// Starts the rotation of the carousel. Direction is the number (+-) of carousel items to rotate by.
	function rotate(direction)
	{	
		this.frontIndex -= direction;
		if (this.frontIndex < 0) { this.frontIndex += items.length; };			
		this.frontIndex %= items.length;
		this.destRotation += ( Math.PI / items.length ) * ( 2*direction );
		this.showFrontText();
		this.go();			
	};
	
	
	function autoRotate()
	{			
		if ( options.autoRotate != 'no' )
		{
			var	dir = (options.autoRotate == 'right')? 1 :-1;
			this.autoRotateTimer = Browser.window.setInterval( function() 
			{				
				ctx.rotate(dir); 
			}, options.autoRotateDelay );
		}
	};
	
	// This is the main loop function that moves everything.
	function updateAll()
	{											
		var	minScale = options.minScale;	// This is the smallest scale applied to the furthest item.
		var smallRange = (1-minScale) * 0.5;
		var	w,h,x,y,scale,item,sinVal;
		
		var	change = (this.destRotation - this.rotation);				
		var	absChange = Math.abs(change);
		//trace(change);
		//this.rotation += change * options.speed;
		//Math.log(
		this.rotation += change  * options.speed ;
		if ( absChange < 0.001 ) { this.rotation = this.destRotation; }		
		//Controller.rotateCount++;
		if ((this.rotation < -1.5*Math.PI) && Controller.logLoop<1)
		{	var lines:String = '';
			Controller.logLoop++;
			trace('logData.length:' + logData.length );
			while (Controller.logData.length > 0)
			{
				lines += Controller.logData.splice(0, 40).join(',')+'\n';
			}
			//Out.log2(lines); 
			Controller.logData = new Array();
		}
		var	itemsLen:Int = items.length;
		var	spacing = (Math.PI / itemsLen) * 2; 
		//var	wrapStyle = null;
		var	radians = this.rotation;
		
	
		// Turn off display. This can reduce repaints/reflows when making style and position changes in the loop.
		// See http://dev.opera.com/articles/view/efficient-javascript/?page=3			
		this.innerWrapper.style.display = 'none';		
		var	style;
		var	px = 'px', reflHeight;	
		var context = this;
		
		for (i in 0...itemsLen)
		{
			item = items[i];
							
			sinVal = Math.sin(radians);
			
			scale = ((sinVal + 1) * smallRange) + minScale;
			//scale = 1;
			
			/*x = this.xCentre + Math.round(( (Math.cos(radians) * this.xRadius) - (item.orgWidth*0.5)) * scale);
			y = this.yCentre + Math.round(sinVal * this.yRadius * scale);					*/
			x = y = 0.0;
	
			if (item.imageOK)
			{
				var	img = item.image;
				w = img.width = Math.round(item.orgWidth * scale);					
				h = img.height = Math.round(item.orgHeight * scale);
				x = this.xCentre + Math.round( Math.cos(radians) * (this.xRadius - (w*0.5 * scale))) - (w*0.5 * scale);
				y = this.yCentre + Math.round(sinVal * this.yRadius * scale);					
				img.style.left = x + 'px';
				img.style.top = y + 'px';
				img.style.zIndex =  Std.string (Math.round(scale * 100)>>0);	// >>0 = Math.foor(). Firefox doesn't like fractional decimals in z-index.
				if (item.reflection != null)
				{																										
					reflHeight = options.reflHeight * scale;						
					style = item.reflection.element.style;
					style.left = x + px;
					style.top = y + h + options.reflGap * scale + px;
					style.width = w + px;								
					if (isMSIE)
					{											
						untyped style.filter.finishy = (reflHeight / h * 100);				
					}else
					{								
						style.height = reflHeight + px;															
					}																													
				}					
			}
			radians += spacing;

			if (i == 0 && Controller.logLoop < 1)
			{
				//trace(Controller.logData.length + ':' + this.rotation + ' < ' + (-1.5*Math.PI));
				//Controller.logData.push(Math.round(y + item.image.height));
			}
			
		}
		// Turn display back on.					
		this.innerWrapper.style.display = 'block';

		// If we have a preceptable change in rotation then loop again next frame.
		if ( absChange >= 0.001 )
		{				
			this.controlTimer = Browser.window.setTimeout( function(){context.updateAll();},this.timeDelay);		
		}else
		{
			// Otherwise just stop completely.				
			this.stop();
		}
	}; // END updateAll		
	
	// Create an Item object for each image	
//		func = function(){return;ctx.updateAll();} ;

	// Check if images have loaded. We need valid widths and heights for the reflections.
	function checkImagesLoaded()
	{
		var	i;
		for(i in 0...images.length) {
			//debug ( images[i].width + ':' + images[i].src +':' +( images[i].complete ? ' yes ':'no'));
			if ( (images[i].width == null) || ( (images[i].complete != null) && (!images[i].complete)  ))
			{
				return;					
			}				
		}
		for(i in 0...images.length) {				
			items.push( new Item( images[i], options ) );	
			J(images[i]).data('itemIndex', i);
			trace(images[i].src + ': ' + i + '::' + items[i].orgWidth + ' x ' + items[i].orgHeight);
		}
		// If all images have valid widths and heights, we can stop checking.			
		Browser.window.clearInterval(tt);
		this.showFrontText();
		this.autoRotate();	
		this.updateAll();
		//this.items = items;
		if(options.callBack != null)
		{
			//debug(options.callBack)
			//options.callBack(J(container),items);
			options.callBack(container,items);
		}
	};
	
}